package com.zty.service.impl;

import com.zty.DTO.FileChunkDTO;
import com.zty.config.ChunkConfig;
import com.zty.errors.BusinessException;
import com.zty.errors.EnumChunkException;
import com.zty.service.FileService;
import com.zty.util.Md5Util;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.FileOutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * @Author ZTY
 * @Description
 * @Date 2:07 2023-12-25
 **/

@Service
public class FileServiceImpl implements FileService {

    private final ChunkConfig chunkConfig;

    //  只需要写锁即可
    private final ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();

    public FileServiceImpl(ChunkConfig chunkConfig) {
        this.chunkConfig = chunkConfig;
    }


    @Override
    public String write(FileChunkDTO fileChunkDTO) {
        String bucketName = fileChunkDTO.getBucketName();
        String filename = fileChunkDTO.getFilename();
        Integer chunkSize = fileChunkDTO.getChunkSize();
        Integer chunkNo = fileChunkDTO.getChunkNo();
        String extension = fileChunkDTO.getExtension();
        byte[] bytes = fileChunkDTO.getBytes();

        //  文件分片落盘时，具体要落到哪个地址
        String chunkPath = buildChunkPath(bucketName, filename, chunkNo, extension);

        File chunkFile = new File(chunkPath);
        //写入磁盘
        try (FileOutputStream fileOutputStream = new FileOutputStream(chunkFile)){

            //  在写文件时，可能会出现并发的情况，现在要争夺写入的权限
            //  要上一把文件的读写锁
            reentrantReadWriteLock.writeLock().lock();

            //  判断根路径所在磁盘是否足够
            if (chunkFile.getFreeSpace() < chunkSize){
                throw new BusinessException(EnumChunkException.DISK_SPACE_NOT_ENOUGH_MEMORY);
            }

            //  判断文件片之前是否已经写入过
            if (!chunkFile.exists()){
                //  返回值为是否创建成功
                boolean created = chunkFile.createNewFile();

                if (!created){
                    throw new BusinessException(EnumChunkException.FAILED_TO_CREATE_CHUNK_FILE);
                }
            }

            //  创建成功后，便可直接将文件分片的字节流写入
            fileOutputStream.write(bytes);

            String md5 = Md5Util.getMd5(bytes);
            //  返回文件分片的字节流大小
            return md5;

        }catch (Exception e){
            throw new BusinessException(EnumChunkException.FAILED_TO_CREATE_CHUNK_FILE);
        }finally {
            //  释放写锁
            reentrantReadWriteLock.writeLock().unlock();
        }
    }

    @Override
    public byte[] read(String filename, String extension, Integer chunkNo, String bucketName) {

        //  将要读取的路径构建出
        String chunkPath = buildChunkPath(bucketName, filename, chunkNo, extension);

        try {
            //  读的时候上一个读锁
            reentrantReadWriteLock.readLock().lock();
            return Files.readAllBytes(Paths.get(chunkPath));

        }catch (Exception e){

            throw new BusinessException(EnumChunkException.FAILED_TO_READ_CHUNK_FILE);
        }finally {

            //  释放读锁
            reentrantReadWriteLock.readLock().unlock();
        }

    }

    /**
     * 定义文件片落盘的地址 命名规则
     * @param bucketName
     * @param filename
     * @param chunkNo
     * @param extension
     * @return
     */
    private String buildChunkPath(String bucketName, String filename, Integer chunkNo,String extension){

        //  工作区路径（根路径）
        //  workspace/bucket_filename_chunkNo.extension（字符串拼接即可）
        return "%s/%s_%s_%s.%s".formatted(chunkConfig.getWorkSpace(),bucketName,filename,chunkNo,extension);
    }

}
